﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using DNT.RavenQA.Core.Extensions;
using DNT.RavenQA.Core.Models;
using DNT.RavenQA.Web.Infrastructure.Commands;
using DNT.RavenQA.Web.Infrastructure.Filters;
using DNT.RavenQA.Web.Infrastructure.Indexing;
using DNT.RavenQA.Web.Infrastructure.QueryModel;
using DNT.RavenQA.Web.ViewModels;
using log4net;
using Raven.Abstractions.Exceptions;
using Raven.Client;

namespace DNT.RavenQA.Web.Controllers
{
    public class QuestionController : ContributeController
    {
        public QuestionController(IDocumentSession session, ILog log)
            : base(session, log)
        {
        }

        public ActionResult ByTag(String id, Int32 pageIndex = 0, Int32 pageSize = 10)
        {
            return ListView(QuestionListViewModel.Build(DocumentSession,
                                                        new QuestionsByTagQuery(id), 
                                                        pageIndex,
                                                        pageSize));
        }

        public ActionResult Hottest(String id, Int32 pageIndex = 0, Int32 pageSize = 10)
        {
            return ListView(QuestionListViewModel.Build(DocumentSession,
                                                        new HottestQuestionsQuery(() => DateTime.UtcNow.AddDays(-3)),
                                                        pageIndex,
                                                        pageSize));
        }

        public ActionResult Index(Int32 pageIndex = 0, Int32 pageSize = 10)
        {
            return ListView(QuestionListViewModel.Build(DocumentSession,
                                                        new LatestQuestionsQuery(),
                                                        pageIndex,
                                                        pageSize));
        }

        private ActionResult ListView(QuestionListViewModel viewModel)
        {
            return View("List", viewModel);
        }

        [UnitOfWork]
        public ActionResult Details(Int32 id)
        {
            DocumentSession.Advanced.UseOptimisticConcurrency = true;
            var command = new SetQuestionAsViewedCommand(new SetQuestionAsViewedCommand.Input
                                                {
                                                    QuestionId = id,
                                                    User = CurrentUser
                                                });

            try { command.Execute(DocumentSession); }
            catch (NonExistingContributeException) { return HttpNotFound(); }
            catch (ConcurrencyException exc)
            {
                Log.Warn("A concurrency exception has been raied.", exc);
                throw;
            }

            return View(QuestionDetailsViewModel.Build(DocumentSession, 
                                                       new AnswersByQuestionQuery(), 
                                                       id, 
                                                       CurrentUser));
        }

        [HttpPost]
        [Authorize]
        [UnitOfWork]
        [ValidateAntiForgeryToken]
        [ValidateInput(false)]
        public ActionResult Add(QuestionInput input)
        {
            input.EnsureIsNotNull("input");

            if (!ModelState.IsValid)
                return AddingQuestionFailed(input);

            input.Author = CurrentUser;
            Int32 questionId;

            try { questionId = new AddQuestionCommand(input).Execute(DocumentSession).Result; }
            catch (Exception exc)
            {
                Log.Error("An error has occurred while adding a new question.", exc);
                throw;
            }

            return RedirectToAction("Details", new {id = questionId});
        }

        [Authorize]
        public ActionResult Add()
        {
            return View(new AddQuestionViewModel {Input = new QuestionInput()});
        }

        private ActionResult AddingQuestionFailed(QuestionInput input)
        {
            ModelState.AddModelError(String.Empty, "Cannot post this question due to validation errors.");
            return View("Add", new AddQuestionViewModel
                            {
                                Input = input/*,
                                ErrorMessage = "Cannot post this question due to validation errors."*/
                            });
        }

        public JsonResult TagsLookup(String inputText, Int32 count)
        {
            if (inputText.IsNullOrWhiteSpace() || count <= 0)
                return Json(new List<String>());

            var result = DocumentSession.Query<QuestionsTagListIndex.ReduceResult, QuestionsTagListIndex>()
                .Where(x => x.Name.StartsWith(inputText))
                .ToList()
                .Select(x => x.Name.ToLowerInvariant());

            return Json(result, JsonRequestBehavior.AllowGet);
        }
    }
}